﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using ERTesting.Dao;
using System.Xml.Linq;
using ERTesting.Model.Config;
using System.IO;

namespace ERTesting.Dao.XmlFileDao
{
    public class XmlTestConfigService : ITestConfigService
    {
        private const string ElementScenario = "scenario";
        private const string ElementInputs = "inputs";
        private const string ElementOutputs = "outputs";
        private const string XmlFileName = "TestsConfig.xml";
        private const string ElementGroups = "groups";
        private const string ElementGroup = "group";
        private const string ElementSheet = "sheet";
        private const string ElementInput = "input";
        private const string ElementOutput = "output";
        private const string AttributeScenarioId = "id";
        private const string AttributeDescription = "description";
        private const string AttributeScenarioType = "type";
        private const string ElementBaseScenarios = "basescenarios";
        private const string AttribubteSpreadsheetMode = "mode";
        private const string AttributeSpreadsheetFilePath = "filename";
        private const string AttributeSheetName = "name";
        private const string ElementRoot = "tests";
        private const string AttributeParamRange = "range";
        private const string AttributeParamValue = "value";
        private const string AttributeParamExpectedValue = "expectedvalue";

        #region ITestConfigService Members

        void ITestConfigService.GetTestGroups(Action<IEnumerable<TestGroup>, Exception> callback)
        {
            try
            {
                FileInfo xmlFile = new FileInfo(XmlFileName);
                if(!xmlFile.Exists)
                {
                    CreateBasicXmlFile();
                }
                var results = from g in XElement.Load(XmlFileName).Element(ElementGroups).Elements(ElementGroup) select new TestGroup((String)g.Attribute(AttributeSheetName));
                callback(results, null);
            }
            catch (FileNotFoundException e)
            {
                callback(null, e);
            }
        }

        private void CreateBasicXmlFile()
        {
            using (XmlWriter xmlWriter = XmlWriter.Create(XmlFileName))
            {
                XDocument document = new XDocument(new XElement(ElementRoot,
                                                                 new XElement(ElementGroups,
                                                                              new XElement(ElementGroup,
                                                                                           new XAttribute(AttributeSheetName, "Default Test Group"), new XElement(ElementSheet, new XAttribute(AttributeSheetName, "Example Sheet"), new XAttribute(AttribubteSpreadsheetMode, SpreadsheetModeEnum.NewWorkbook), new XAttribute(AttributeSpreadsheetFilePath, ""), new XElement(ElementScenario, new XAttribute(AttributeScenarioId, "1"), new XAttribute(AttributeDescription, "An example scenario."), new XAttribute(AttributeScenarioType, "Mandatory"), new XElement(ElementBaseScenarios), new XElement(ElementInputs, new XElement(ElementInput, new XAttribute(AttributeParamRange, "A1"), new XAttribute(AttributeParamValue, 1.0))), new XElement(ElementOutputs, new XElement(ElementOutput, new XAttribute(AttributeParamRange, "A1"), new XAttribute(AttributeParamExpectedValue, 1.0)))))))));
                document.WriteTo(xmlWriter);
                xmlWriter.Flush();
                xmlWriter.Close();
            }
        }

        void ITestConfigService.GetSheetsForGroup(TestGroup testGroup, Action<IEnumerable<SheetConfig>, Exception> callback)
        {
            try
            {

                var results = from s in XElement.Load(XmlFileName).Element(ElementGroups).Elements(ElementGroup).Elements(ElementSheet) where ((String)s.Parent.Attribute(AttributeSheetName)) == testGroup.Name select new SheetConfig((String)s.Attribute(AttributeSheetName), (SpreadsheetModeEnum)Enum.Parse(typeof(SpreadsheetModeEnum), (string)s.Attribute(AttribubteSpreadsheetMode)), (String)s.Attribute(AttributeSpreadsheetFilePath));
                callback(results, null);
            }
            catch (FileNotFoundException e)
            {
                callback(null, e);
            }
        }



        void ITestConfigService.GetScenariosForSheet(SheetConfig sheetConfig, Action<IEnumerable<Scenario>, Exception> callback)
        {
            try
            {
                var results = from s in XElement.Load(XmlFileName).Element(ElementGroups).Elements(ElementGroup).Elements(ElementSheet).Elements(ElementScenario) where ((String)s.Parent.Attribute(AttributeSheetName)) == sheetConfig.Name select new Scenario((String)s.Attribute(AttributeScenarioId), (String)s.Attribute(AttributeDescription), (ScenarioType)Enum.Parse(typeof(ScenarioType),(string)s.Attribute(AttributeScenarioType), true), from b in s.Element(ElementBaseScenarios).Elements(ElementScenario) select (string)b.Attribute(AttributeScenarioId));
                callback(results, null);
            }
            catch (FileNotFoundException e)
            {
                callback(null, e);
            }
        }

        void ITestConfigService.GetInputParametersForScenario(Scenario scenario, Action<IEnumerable<TestParameter>, Exception> callback, bool includeBaseScenarios)
        {
            try
            {
                XElement doc = XElement.Load(XmlFileName);
                var results = from i in doc.Element(ElementGroups).Elements(ElementGroup).Elements(ElementSheet).Elements(ElementScenario).Elements(ElementInputs).Elements(ElementInput) where ((String)i.Parent.Parent.Attribute(AttributeScenarioId)) == scenario.ScenarioId select new TestParameter((String)i.Attribute(AttributeParamRange), (String)i.Attribute(AttributeParamValue));
                callback(results, null);
            }
            catch (FileNotFoundException e)
            {
                callback(null, e);
            }
        }


        /// <summary>
        /// Gets all the output parameters for a scenario.
        /// </summary>
        /// <param name="scenario">The scenario to get the parameters for.</param>
        /// <param name="callback">The callback action to pass the results to.</param>
        /// <param name="includeBaseScenarios">If true the output parameters for the base scenarios are returned in the set.</param>
        void ITestConfigService.GetOutputParametersForScenario(Scenario scenario, Action<IEnumerable<TestOutputParameter>, Exception> callback, bool includeBaseScenarios)
        {
            try
            {
                XElement doc = XElement.Load(XmlFileName);
                var results = from i in doc.Element(ElementGroups).Elements(ElementGroup).Elements(ElementSheet).Elements(ElementScenario).Elements(ElementOutputs).Elements(ElementOutput) where ((String)i.Parent.Parent.Attribute(AttributeScenarioId)) == scenario.ScenarioId select new TestOutputParameter((String)i.Attribute(AttributeParamRange));
                callback(results, null);
            }
            catch (FileNotFoundException e)
            {
                callback(null, e);
            }
        }

        /// <summary>
        /// Saves a test group to store.
        /// </summary>
        /// <param name="testGroup"></param>
        void ITestConfigService.SaveTestGroup(TestGroup testGroup)
        {
                XElement xElement = XElement.Load(XmlFileName);
                if (xElement == null)
                {
                    throw new ApplicationException("Could not load xml store file.");
                }
                xElement.Element(ElementGroups).Add(new XElement(ElementGroup, new XAttribute(AttributeSheetName, testGroup.Name)));
                xElement.Save(XmlFileName);
        }

        void ITestConfigService.RemoveSheetConfig(SheetConfig sheetConfig)
        {
            
        }

        #endregion
    }
}
